/*
 * @Author: 生俊甫 1758142861@qq.com
 * @Date: 2024-06-12 15:09:00
 * @LastEditors: 生俊甫 1758142861@qq.com
 * @LastEditTime: 2024-06-12 20:06:34
 * @FilePath: /sjf/2024-project/2024_centos/source/matcher.hpp
 * @Description: 这是默认设置,请设置`customMade`, 打开koroFileHeader查看配置 进行设置: https://github.com/OBKoro1/koro1FileHeader/wiki/%E9%85%8D%E7%BD%AE
 */
#pragma once

#include <iostream>
#include <mutex>
#include <list>
#include <thread>
#include <unordered_map>
#include <condition_variable>
#include "./db/db.hpp"
#include "./user.hpp"
#include "./room.hpp"
#include "./log/logger.hpp"

/**
 * 该模块表示的是对匹配对战管理的模块
 * 分为了三个不同的分段
 *      <2000            超凡
 *      >=2000 && <3000  神话 
 *      >=3000           赋能
 * 其中对于matcher类来讲
 *      是对一个分段队列操作的内容
 * 对于matcher_manager
 *      则是具体针对不同分段队列的增删操作
 *      和匹配到相同分段后对匹配到一个房间的两人的响应
*/

/**
 * 对于一个段位的匹配队列模块
 * 使用的是list代替queue
 * 方便查找中间元素
 * 并规定list只能头入头出
 */
template<class T>
class matcher
{
public:
    int size()
    {
        std::unique_lock<std::mutex> lock(_mutex);
        return _list.size();
    }
    //添加玩家，并唤醒队列，看人数够不够进行匹配
    void push(const T& data)
    {
        std::unique_lock<std::mutex> lock(_mutex);
        _list.push_back(data);
        _cond.notify_all();
    }
    //输出性参数，将出队列的元素返回
    bool pop(T& data)
    {
        std::unique_lock<std::mutex> lock(_mutex);
        if(_list.empty() == true)
            return false;
        data = _list.front();
        _list.pop_front();
        return true;
    }
    bool empty()
    {
        std::unique_lock<std::mutex> lock(_mutex);
        return _list.empty();
    }
    //一般是玩家不够进行匹配，将玩家阻塞起来
    void wait()
    {
        std::unique_lock<std::mutex> lock(_mutex);
        _cond.wait(lock);
    }
    //移除某一特定数据
    void remove(T& data)
    {
        std::unique_lock<std::mutex> lock(_mutex);
        _list.remove(data);
    }
private:
    std::list<T> _list;//使用链表代替queue，为了方便对中间元素的操作
    std::mutex _mutex;
    std::condition_variable _cond;//该条件变量是为了阻塞消费者,队列中元素个数<2,则被阻塞
};

class matcher_manager
{
public:
    matcher_manager(user_table* user,online_user* online_user,room_manage* room)
    :_user(user),_room(room),_online_user(online_user)
    ,_1t_extraordinary(std::thread(&matcher_manager::th_extraordinary_entry,this))
    ,_2t_mythology(std::thread(&matcher_manager::th_mythology_entry,this))
    ,_3t_empowerment(std::thread(&matcher_manager::th_empowerment_entry,this))
    {
        DBG_LOG("游戏匹配机制初始完成.");
    }
    ~matcher_manager()
    {
        DBG_LOG("游戏匹配机制析构完成.");
    }
    bool add(uint64_t uid)
    {
        //获取玩家段位
        Json::Value user;
        bool ret = _user->select_user_by_id(uid,user);
        if(ret == false)
        {
            DBG_LOG("获取玩家id:%d 时发生错误",uid);
            return false;
        }
        int score = user["score"].asInt();
        //根据段位进行分队
        if(score < 2000)
            _1q_extraordinary.push(uid);
        else if(score >= 2000 && score < 3000)
            _2q_mythology.push(uid);
        else 
            _3q_empowerment.push(uid);
        return true;
    }
    bool del(uint64_t uid)
    {
        //获取玩家段位
        Json::Value user;
        bool ret = _user->select_user_by_id(uid,user);
        if(ret == false)
        {
            DBG_LOG("获取玩家id:%d 时发生错误",uid);
            return false;
        }
        int score = user["score"].asInt();
        //根据段位进行分队
        if(score < 2000)
            _1q_extraordinary.remove(uid);
        else if(score >= 2000 && score < 3000)
            _2q_mythology.remove(uid);
        else 
            _3q_empowerment.remove(uid);
        return true;
    }
private:
    /**
     * 真正的匹配实现
     * 三个匹配队列实现思想是一样的所有统一实现
     */
    void handler_matcher(matcher<uint64_t>& queuee)
    {
        while(1)
        {
            //1.先判断队列中人数
            while(queuee.size() < 2) { queuee.wait(); }
            //2.出队两个玩家
            uint64_t uid1,uid2;
            bool ret = queuee.pop(uid1);
            if(ret == false) continue;
            ret = queuee.pop(uid2);
            if(ret == false) { this->add(uid1);continue; }
            //3.再次检测这两位玩家的状态，如果掉线，则取消匹配，将另一位重新入队
            wsserver_t::connection_ptr conn1 = _online_user->get_conn_in_hall(uid1);
            if(conn1.get() == nullptr)
            {
                this->add(uid2);
                continue;
            }
            wsserver_t::connection_ptr conn2 = _online_user->get_conn_in_hall(uid2);
            if(conn2.get() == nullptr)
            {
                this->add(uid1);
                continue;
            }
            //4.为两位玩家创建房间
            room_ptr rp = _room->create_room(uid1,uid2);
            if(rp.get() == nullptr)
            {
                this->add(uid1);
                this->add(uid2);
                continue;
            }
            //5.对这两名玩家进行响应
            Json::Value resp;
            resp["optype"] = "match_success";
            resp["result"] = true;
            std::string body;
            util_json::serialize(resp, body);
            conn1->send(body);
            conn2->send(body);
            //
        }
    }
    void th_extraordinary_entry() { return handler_matcher(_1q_extraordinary);}
    void th_mythology_entry() { return handler_matcher(_2q_mythology);}
    void th_empowerment_entry() { return handler_matcher(_3q_empowerment);}
private:
    matcher<uint64_t> _1q_extraordinary;//超凡队列
    matcher<uint64_t> _2q_mythology;//神话队列
    matcher<uint64_t> _3q_empowerment;//赋能队列
    std::thread _1t_extraordinary;//超凡队列线程
    std::thread _2t_mythology;//神话队列线程
    std::thread _3t_empowerment;//赋能队列线程
    user_table* _user;
    online_user* _online_user;
    room_manage* _room;
};